#!/usr/bin/python3 -W ignore::DeprecationWarning
# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.


import os
import sys
import glob
from random import choice
import string
from optparse import OptionParser
import mysql.connector
import paramiko
from threading import Thread

# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
# ---- We do this so cloud_utils can be looked up in the following order:
# ---- 1) Sources directory
# ---- 2) waf configured PYTHONDIR
# ---- 3) System Python path
for pythonpath in (
    "@PYTHONDIR@",
    os.path.join(
        os.path.dirname(__file__), os.path.pardir, os.path.pardir, "python", "lib"
    ),
):
    if os.path.isdir(pythonpath):
        sys.path.insert(0, pythonpath)
# ---- End snippet of code ----
from cloud_utils import check_call, CalledProcessError, read_properties

cfg = "@MSCONF@/db.properties"

# ---------------------- option parsing and command line checks ------------------------


usage = """%prog <license file> <-a | host names / IP addresses...>

This command deploys the license file specified in the command line into a specific XenServer host or all XenServer hosts known to the management server."""

parser = OptionParser(usage=usage)
parser.add_option(
    "-a",
    "--all",
    action="store_true",
    dest="all",
    default=False,
    help="deploy to all known hosts rather that a single host",
)

# ------------------ functions --------------------


def e(msg):
    parser.error(msg)


def getknownhosts(host, username, password):
    conn = mysql.connector.connect(host=host, user=username, password=password)
    cur = conn.cursor()
    cur.execute(
        "SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'username' and setup = 1"
    )
    usernames = dict(cur.fetchall())
    cur.execute(
        "SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'password' and setup = 1"
    )
    passwords = dict(cur.fetchall())
    creds = dict([[x, (usernames[x], passwords[x])] for x in list(usernames.keys())])
    cur.close()
    conn.close()
    return creds


def splitlast(string, splitter):
    splitted = string.split(splitter)
    first, last = splitter.join(splitted[:-1]), splitted[-1]
    return first, last


def parseuserpwfromhosts(hosts):
    creds = {}
    for host in hosts:
        user = "root"
        password = ""
        if "@" in host:
            user, host = splitlast(host, "@")
            if ":" in user:
                user, password = splitlast(user, ":")
        creds[host] = (user, password)
    return creds


class XenServerConfigurator(Thread):

    def __init__(self, host, user, password, keyfiledata):
        Thread.__init__(self)
        self.host = host
        self.user = user
        self.password = password
        self.keyfiledata = keyfiledata
        self.retval = None  # means all's good
        self.stdout = ""
        self.stderr = ""
        self.state = "initialized"

    def run(self):
        try:
            self.state = "running"
            c = paramiko.SSHClient()
            c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            c.connect(self.host, username=self.user, password=self.password)
            sftp = c.open_sftp()
            sftp.chdir("/tmp")
            f = sftp.open("xen-license", "w")
            f.write(self.keyfiledata)
            f.close()
            sftp.close()
            stdin, stdout, stderr = c.exec_command(
                "xe host-license-add license-file=/tmp/xen-license"
            )
            c.exec_command("false")
            self.stdout = stdout.read(-1)
            self.stderr = stderr.read(-1)
            self.retval = stdin.channel.recv_exit_status()
            c.close()
            if self.retval != 0:
                self.state = "failed"
            else:
                self.state = "finished"

        except Exception as e:
            self.state = "failed"
            self.retval = e
            # raise

    def __str__(self):
        if self.state == "failed":
            return "<%s XenServerConfigurator on %s@%s: %s>" % (
                self.state,
                self.user,
                self.host,
                str(self.retval),
            )
        else:
            return "<%s XenServerConfigurator on %s@%s>" % (
                self.state,
                self.user,
                self.host,
            )


# ------------- actual code --------------------

(options, args) = parser.parse_args()

try:
    licensefile, args = args[0], args[1:]
except IndexError:
    e("The first argument must be the license file to use")

if options.all:
    if len(args) != 0:
        e("IP addresses cannot be specified if -a is specified")
    config = read_properties(cfg)
    creds = getknownhosts(
        config["db.cloud.host"],
        config["db.cloud.username"],
        config["db.cloud.password"],
    )
    hosts = list(creds.keys())
else:
    if not args:
        e("You must specify at least one IP address, or -a")
    hosts = args
    creds = parseuserpwfromhosts(hosts)

try:
    keyfiledata = file(licensefile).read(-1)
except OSError as e:
    sys.stderr.write("The file %s cannot be opened" % licensefile)
    sys.exit(1)

configurators = []
for host, (user, password) in list(creds.items()):
    configurators.append(XenServerConfigurator(host, user, password, keyfiledata))


for c in configurators:
    c.start()

for c in configurators:
    print(c.host + "...", end=" ")
    c.join()
    if c.state == "failed":
        if c.retval:
            msg = "failed with return code %s: %s%s" % (c.retval, c.stdout, c.stderr)
            msg = msg.strip()
            print(msg)
        else:
            print("failed: %s" % c.retval)
    else:
        print("done")

successes = len([a for a in configurators if not a.state == "failed"])
failures = len([a for a in configurators if a.state == "failed"])

print("%3s successes" % successes)
print("%3s failures" % failures)
